﻿namespace NetFx.SMIL.Definitions
{
  using System;
  using System.Text;
  using System.Collections;
  using System.Collections.Generic;

  using System.Reflection;
  using System.Reflection.Emit;
  using System.Runtime.InteropServices;

  using System.Diagnostics;
  using System.Diagnostics.CodeAnalysis;
  using System.Diagnostics.SymbolStore;
  using System.Security;
  using System.Security.Permissions;

  using NetFx.MSIL;
  using NetFx.SMIL.References;

  public class FieldDef:MemberDef 
  {
    #region Statics
    public static FieldDef of(FieldInfo fieldInfo)
    {
      return new FieldDef(fieldInfo);
    }
    public static FieldDef of(string name)
    {
      return new FieldDef(name);
    }
    #endregion
    #region Fields
    protected FieldAttributes attributes;
    protected TypeRef fieldType = null;
    protected List<TypeRef> optionalCustomModifiers = null;
    protected List<TypeRef> requiredCustomModifiers = null;
    protected int size =0;
    protected byte[] data = null;
    protected int offset = 0;
    protected bool initialized = false;
    protected object defaultValue = null;
    #endregion
    #region Properties
    public override DefType DefinitionType
    {
      get { return DefType.Field; }
    }
    public virtual FieldAttributes Attributes
    {
      get
      {
        return this.attributes;
      }
      set
      {
        this.attributes = value;
      }
    }
    public virtual TypeRef FieldType
    {
      get
      {
        return this.fieldType;
      }
      set
      {
        this.fieldType = value;
      }
    }
    public virtual int StaticSize
    {
      get
      {
        return this.size;
      }
      set
      {
        this.size = value;
      }
    }
    public virtual bool IsInitialized
    {
      get
      {
        return this.initialized;
      }
      set
      {
        this.initialized = value;
      }
    }
    public virtual byte[] StaticData
    {
      get
      {
        return this.data;
      }
      set
      {
        this.data = value;
      }
    }
    public virtual object DefaultValue
    {
      get
      {
        return this.defaultValue;
      }
      set
      {
        this.defaultValue = value;
      }
    }
    public virtual int FieldOffset
    {
      get
      {
        return this.offset;
      }
      set
      {
        this.offset = value;
      }
    }
    public virtual List<TypeRef> OptionalCustomModifiers
    {
      get
      {
        return this.optionalCustomModifiers == null
          ? this.optionalCustomModifiers = new List<TypeRef>() 
          : this.optionalCustomModifiers;
      }
    }
    public virtual List<TypeRef> RequiredCustomModifiers
    {
      get
      {
        return this.requiredCustomModifiers == null
          ? this.requiredCustomModifiers = new List<TypeRef>()
          : this.requiredCustomModifiers;
      }
    }
    public virtual bool IsAssembly
    {
      get
      {
        return ((this.Attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Assembly);
      }
      set
      {
        if (value)
        {
          this.attributes |= FieldAttributes.Assembly;
        }
        else
        {
          this.attributes = this.ClearFieldAccess(this.attributes);
        }
      }
    }
    public virtual bool IsFamilyAndAssembly
    {
      get
      {
        return ((this.Attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.FamANDAssem);
      }
      set
      {
        if (value)
        {
          this.attributes |= FieldAttributes.FamANDAssem;
        }
        else
        {
          this.attributes = this.ClearFieldAccess(this.attributes);
        }
      }
    }
    public virtual bool IsFamilyOrAssembly
    {
      get
      {
        return ((this.Attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.FamORAssem);
      }
      set
      {
        if (value)
        {
          this.attributes |= FieldAttributes.FamORAssem;
        }
        else
        {
          this.attributes = this.ClearFieldAccess(this.attributes);
        }
      }
    }
    public virtual bool IsFamily
    {
      get
      {
        return ((this.Attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Family);
      }
      set
      {
        if (value)
        {
          this.attributes |= FieldAttributes.Family;
        }
        else
        {
          this.attributes = this.ClearFieldAccess(this.attributes);
        }
      }
    }
    public virtual bool HasDefault
    {
      get
      {
        return ((this.Attributes & FieldAttributes.HasDefault) != 0);
      }
      set
      {
        if (value)
        {
          this.attributes |= FieldAttributes.HasDefault;
        }
        else
        {
          this.attributes &= (~FieldAttributes.HasDefault);
        }
      }
    }
    public virtual bool HasFieldMarshal
    {
      get
      {
        return ((this.Attributes & FieldAttributes.HasFieldMarshal) != 0);
      }
      set
      {
        if (value)
        {
          this.attributes |= FieldAttributes.HasFieldMarshal;
        }
        else
        {
          this.attributes &= (~FieldAttributes.HasFieldMarshal);
        }
      }
    }
    public virtual bool HasFieldRVA
    {
      get
      {
        return ((this.Attributes & FieldAttributes.HasFieldRVA) != 0);
      }
      set
      {
        if (value)
        {
          this.attributes |= FieldAttributes.HasFieldRVA;
        }
        else
        {
          this.attributes &= (~FieldAttributes.HasFieldRVA);
        }
      }
    }
    public virtual bool IsInitOnly
    {
      get
      {
        return ((this.Attributes & FieldAttributes.InitOnly) != FieldAttributes.PrivateScope);
      }
      set
      {
        if (value)
        {
          this.attributes |= FieldAttributes.InitOnly;
        }
        else
        {
          this.attributes &= (~FieldAttributes.InitOnly);
        }
      }
    }
    public virtual bool IsLiteral
    {
      get
      {
        return ((this.Attributes & FieldAttributes.Literal) != FieldAttributes.PrivateScope);
      }
      set
      {
        if (value)
        {
          this.attributes |= FieldAttributes.Literal;
        }
        else
        {
          this.attributes &= (~FieldAttributes.Literal);
        }
      }
    }
    public virtual bool IsNotSerialized
    {
      get
      {
        return ((this.Attributes & FieldAttributes.NotSerialized) != FieldAttributes.PrivateScope);
      }
      set
      {
        if (value)
        {
          this.attributes |= FieldAttributes.NotSerialized;
        }
        else
        {
          this.attributes &= (~FieldAttributes.NotSerialized);
        }
      }
    }
    public virtual bool IsPinvokeImpl
    {
      get
      {
        return ((this.Attributes & FieldAttributes.PinvokeImpl) != FieldAttributes.PrivateScope);
      }
      set
      {
        if (value)
        {
          this.attributes |= FieldAttributes.PinvokeImpl;
        }
        else
        {
          this.attributes &= (~FieldAttributes.PinvokeImpl);
        }
      }
    }
    public virtual bool IsPrivate
    {
      get
      {
        return ((this.Attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Private);
      }
      set
      {
        if (value)
        {
          this.attributes |= FieldAttributes.Private;
        }
        else
        {
          this.attributes = this.ClearFieldAccess(this.attributes);
        }
      }
    }
    //public virtual bool IsPrivateScope
    //{
    //  get
    //  {
    //    return (this.Attributes==this.ClearFieldAccess(this.Attributes) );
    //  }
    //  set
    //  {
    //    this.attributes = this.ClearFieldAccess(this.attributes);
    //  }
    //}
    public virtual bool IsPublic
    {
      get
      {
        return ((this.Attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Public);
      }
      set
      {
        if (value)
        {
          this.attributes |= FieldAttributes.Public;
        }
        else
        {
          this.attributes = this.ClearFieldAccess(this.attributes);
        }
      }
    }
    public virtual bool IsRTSpecialName
    {
      get
      {
        return ((this.Attributes & FieldAttributes.RTSpecialName) != 0);
      }
      set
      {
        if (value)
        {
          this.attributes |= FieldAttributes.RTSpecialName;
        }
        else
        {
          this.attributes &= (~FieldAttributes.RTSpecialName);
        }
      }
    }
    public virtual bool IsSpecialName
    {
      get
      {
        return ((this.Attributes & FieldAttributes.SpecialName) != FieldAttributes.PrivateScope);
      }
      set
      {
        if (value)
        {
          this.attributes |= FieldAttributes.SpecialName;
        }
        else
        {
          this.attributes &= (~FieldAttributes.SpecialName);
        }
      }
    }
    public virtual bool IsStatic
    {
      get
      {
        return ((this.Attributes & FieldAttributes.Static) != FieldAttributes.PrivateScope);
      }
      set
      {
        if (value)
        {
          this.attributes |= FieldAttributes.Static;
        }
        else
        {
          this.attributes &= (~FieldAttributes.Static);
        }
      }
    }
    #endregion
    #region Constructors
    public FieldDef(string name) : base(name) { }
    public FieldDef(FieldInfo fieldInfo):this(String.Empty)
    {
      this.OnReflecting(fieldInfo);
    }
    public FieldDef(string name,Type fieldType):this(name)
    {
      this.fieldType = TypeRef.of(fieldType);
    }
    public FieldDef(string name,Type fieldType,FieldAttributes attributes):this(name,fieldType)
    {
      this.attributes = attributes;
    }
    #endregion
    #region Methods
    protected override AnyGenerator OnCompleting(AnyGenerator generator)
    {
      if (generator is FieldGenerator)
      {
        ((FieldGenerator)generator).DefaultValue = this.DefaultValue;
        ((FieldGenerator)generator).FieldOffset = this.FieldOffset;
      }
      return base.OnCompleting(generator);
    }
    protected override AnyGenerator OnDefining(AnyGenerator container)
    {
      if (container is TypeGenerator)
      {
        TypeGenerator t = container as TypeGenerator;

        if (this.IsStatic)
        {
          return this.IsInitialized
            ? t.DefineInitializedStaticField(
                this.Name,
                this.StaticData,
                this.Attributes
                )
            : t.DefineUninitializedStaticField(
                this.Name,
                this.StaticSize,
                this.Attributes
                )
            ;
        }
        else
        {
          return t.DefineField(this.Name, this.FieldType.ReflectInfo, this.attributes);
        }
      }
      return base.OnDefining(container);
    }
    protected virtual FieldAttributes ClearFieldAccess(FieldAttributes attrib)
    {
      return attrib & (~FieldAttributes.FieldAccessMask);
    }
    protected virtual FieldAttributes ClearReserved(FieldAttributes attrib)
    {
      return attrib & (~FieldAttributes.ReservedMask);
    }

    protected override ReflectDef OnReflecting(object source)
    {
      if (source is FieldInfo)
      {
        FieldInfo fi = source as FieldInfo;
        this.attributes = fi.Attributes;
        this.fieldType = TypeRef.of(fi.FieldType);

        this.ReloadMembers(fi.GetOptionalCustomModifiers(), this.OptionalCustomModifiers);
        this.ReloadMembers(fi.GetRequiredCustomModifiers(), this.RequiredCustomModifiers);
      }
      return base.OnReflecting(source);
    }
    protected override void DisposeList(bool disposing)
    {
      if (disposing)
      {
        this.DisposeList(this.OptionalCustomModifiers);
        this.DisposeList(this.RequiredCustomModifiers);
      }
    }
    protected override void OnBreakingReference()
    {
      this.Break(this.FieldType);
      this.Break(this.OptionalCustomModifiers);
      this.Break(this.RequiredCustomModifiers);
      base.OnBreakingReference();
    }
    #endregion
  }
}
